package com.newly.center.sys.service.menu.api;

import com.newly.common.base.constants.MenuConstant;
import com.newly.common.base.entity.sys.menu.dto.MenuDto;
import com.newly.common.base.entity.sys.menu.dto.MenuMetaDto;
import com.newly.common.base.entity.sys.menu.po.MenuPo;
import com.newly.common.base.entity.sys.menu.vo.MenuAbilityVo;
import com.newly.common.base.entity.sys.menu.vo.MenuRenderRuleVo;
import com.newly.common.base.entity.sys.menu.vo.MenuTypeVo;
import com.newly.common.base.entity.sys.menu.xml.MenuItemsXml;
import com.newly.common.base.entity.sys.tenant.bo.SimpleTenantBo;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.Map;

/**
 * @author gjm guan
 * @date 2021/9/27 16:26
 * @desc 菜单管理接口
 */
public interface IMenuService {

    /**
     * 创建菜单，此操作只有开发者才能操作
     * 需要遵守创建规则，菜单的规则限制都在创建菜单中进行
     * 如果违反规则，后面可能会导致混乱
     * 规则： 目录的父菜单可以是目录，菜单的父菜单只能是目录，按钮的父菜单是菜单
     *
     * @param menuDto 菜单数据
     * @return 创建成功返回true
     */
    Integer save(MenuDto menuDto);

    /**
     * 批量创建菜单，此操作会递归获取menuDtos中的子菜单创建
     *
     * @param menuDtos 菜单数据
     * @return 创建成功返回true
     */
    boolean saveAll(List<MenuDto> menuDtos);

    /**
     * 创建子菜单，会递归获取子菜单将子菜单存储至数据库中
     *
     * @param parentMenuId    父菜单id
     * @param childrenMenuDto 子菜单数据
     * @return 创建完成返回true
     */
    void saveChildren(Integer parentMenuId, List<MenuDto> childrenMenuDto);

    /**
     * 刷新菜单缓存，从数据库中重新将菜单数据加载
     * 至redis中，此操作只有开发者才能操作
     *
     * @return 刷新成功返回true, 失败会抛出错误原因
     */
    boolean refreshCache();

    /**
     * 修改菜单排序值
     *
     * @param menuId 菜单id
     * @param sort   排序值
     * @return 修改成功返回true
     */
    boolean modifySortById(Integer menuId, Integer sort);

    /**
     * 通过id修改禁用或启用值
     *
     * @param menuIds 菜单id
     * @param enable  禁用或启用
     * @return 修改成功返回true
     */
    boolean modifyEnableById(List<Integer> menuIds, Integer enable);

    /**
     * 通过id修改访问能力
     *
     * @param menuIds 菜单id
     * @param access  访问能力
     * @return 修改成功返回true
     */
    boolean modifyAccessById(List<Integer> menuIds, Integer access);

    /**
     * 修改父子联动属性
     *
     * @param menuDto 菜单数据
     * @return 修改成功返回true
     */
    boolean modifyLinkageProperties(MenuDto menuDto);

    /**
     * 通过id修改菜单，此操作只有开发者才能进行
     *
     * @param menuDto 菜单数据
     * @return 修改成功返回true
     */
    boolean modifyById(MenuDto menuDto);

    /**
     * 修改菜单元数据
     *
     * @param menuMetaDto 菜单元数据
     * @return 修改成功返回true
     */
    boolean modifyMeta(MenuMetaDto menuMetaDto);

    /**
     * 删除菜单，也会删除它的所有子菜单
     *
     * @param menuIds 菜单id
     * @return 删除成功返回true
     */
    boolean removeByIds(List<Integer> menuIds);

    /**
     * 解析menuPos创建人id，去重
     *
     * @return 创建人id
     */
    List<Long> parseCreateTenantIds(List<MenuPo> menuPos);

    /**
     * 解析menuPos菜单id
     *
     * @return 菜单id
     */
    List<Integer> parseIds(List<MenuPo> menuPos);

    /**
     * 分页带条件查询菜单，此操作只有开发者才能进行
     *
     * @param offset      页码
     * @param limit       页数
     * @param abilityCode 能力编码
     * @param typeCode    类型编码
     * @param createBy    创建人
     * @param keyword     关键字
     * @return PageUtils List MenuPo.class
     */
//    PageUtils list(Integer offset, Integer limit, Integer abilityCode, Integer typeCode,
//                   Long createBy, String keyword);
    List<MenuPo> list(Integer offset, Integer limit, Integer abilityCode, Integer typeCode,
                      Long createBy, String keyword);

    /**
     * 封装menuPos的子菜单,parentMenuPo为顶级菜单数据
     * 子菜单数据会从数据库中获取
     *
     * @param parentMenuPo 父菜单数据
     */
    void packageDBChildren(List<MenuPo> parentMenuPo);

    /**
     * 将menuPos中的所有数据进行层级父子关系封装
     *
     * @param menuPos 菜单数据
     * @return List MenuPo.class
     */
    List<MenuPo> packageSourceChildren(List<MenuPo> menuPos);

    /**
     * 从childrenMenuPo集合中找parentMenuPo的子菜单
     * 存在递归操作，会继续从childrenMenuPo中选择顶级菜单
     * 继续封装机子菜单
     *
     * @param parentMenuPo   父菜单
     * @param childrenMenuPo 子菜单列表
     * @return List MenuPo.class 子菜单
     */
    List<MenuPo> packageSourceChildren(MenuPo parentMenuPo, List<MenuPo> childrenMenuPo);

    /**
     * 递归获取menuIds的所有子菜单，但是不进行层级封装，
     * 而是将所有数据封装进返回值中
     *
     * @param parentMenuPos 父菜单数据
     * @return 所有子菜单数据
     */
    List<MenuPo> getChildren(List<MenuPo> parentMenuPos);

    /**
     * 无条件获取所有菜单
     *
     * @return List MenuPo.class
     */
    List<MenuPo> getAll();

    /**
     * 通过id获取菜单
     *
     * @param menuIds 菜单id
     * @return List MenuPo.class
     */
    List<MenuPo> getByIds(List<Integer> menuIds);

    /**
     * 通过id获取菜单
     *
     * @param menuId 菜单id
     * @return MenuPo.class
     */
    MenuPo getById(Integer menuId);

    /**
     * 通过id获取菜单权限标识
     *
     * @param menuIds 菜单id
     * @return 菜单权限标识
     */
    List<String> getAuthByIds(List<Integer> menuIds);

    /**
     * 从当前租户权限中获取菜单
     * 会先尝试从redis中获取，redis中获取不到再从数据库中获取
     *
     * @return List MenuPo.class
     */
    List<MenuPo> getInAuth();

    /**
     * 从当前租户菜单获取id为menuId的菜单
     *
     * @param menuId 菜单id
     * @return MenuPo.class
     */
    MenuPo getByIdInAuth(Integer menuId);

    /**
     * 从当前租户菜单获取id为menuIds的菜单
     *
     * @param menuIds 菜单id
     * @return MenuPo.class
     */
    List<MenuPo> getByIdsInAuth(List<Integer> menuIds);

    /**
     * 从当前租户菜单获取父id为menuId的菜单
     *
     * @param parentMenuId 菜单id
     * @return List MenuPo.class
     */
    List<MenuPo> getByParentIdInAuth(Integer parentMenuId);

    /**
     * 从当前租户菜单获取父id为menuIds的菜单
     *
     * @param parentMenuIds 菜单id
     * @return List MenuPo.class
     */
    List<MenuPo> getByParentIdsInAuth(List<Integer> parentMenuIds);

    /**
     * 从获取中获取id为menuIds的菜单
     *
     * @param menuIds 菜单id
     * @return List MenuPo.class
     */
    List<MenuPo> getByIdsInCache(List<Integer> menuIds);

    /**
     * 获取菜单类型
     *
     * @return List MenuTypeVo.class
     */
    List<MenuTypeVo> getTypes();

    /**
     * 通过类型编码获取菜单类型详情数据
     *
     * @param typeCode 菜单类型编码
     * @return MenuTypeVo.class
     */
    MenuTypeVo getTypeByCode(Integer typeCode);

    /**
     * 查询菜单能力
     *
     * @return List MenuAbilityVo.class
     */
    List<MenuAbilityVo> getAbility();

    /**
     * 通过能力编码获取菜单能力详情数据
     *
     * @param abilityCode 菜单能力编码
     * @return MenuAbilityVo.class
     */
    MenuAbilityVo getAbilityByCode(Integer abilityCode);

    /**
     * 通过能力编码获取菜单能力详情数据
     *
     * @param ability 菜单能力枚举
     * @return MenuAbilityVo.class
     */
    MenuAbilityVo getAbility(MenuConstant.Ability ability);

    /**
     * 通过类型编码获取菜单能力详情数据，会根据规则来获取到能力编码
     *
     * @param typeCode 菜单类型编码
     * @return MenuAbilityVo.class
     */
    List<MenuAbilityVo> getAbilityByTypeCode(Integer typeCode);

    /**
     * 创建所有菜单创建人，此操作只有开发者才能进行
     *
     * @return List SimpleTenantBo.class
     */
    List<SimpleTenantBo> getCreateTenant();

    /**
     * 筛选侧边栏菜单
     *
     * @param menuPos 源菜单数据
     * @return List MenuPo.class
     */
    List<MenuPo> chooseLayout(List<MenuPo> menuPos);

    /**
     * 筛选权限列表菜单数据
     *
     * @param menuPos 源菜单数据
     * @return List MenuPo.class
     */
    List<MenuPo> choosePermList(List<MenuPo> menuPos);

    /**
     * 查询当前租户侧边栏菜单
     *
     * @return List MenuPo.class
     */
    List<MenuPo> getLayoutInAuth();

    /**
     * 查询当前租户权限列表使用的菜单
     *
     * @return List MenuPo.class
     */
    List<MenuPo> getPermListInAuth();

    /**
     * 封装layout菜单,此步做辅助性工作，例如给一级目录赋予相应的值
     *
     * @param layoutMenuPos 所有layout菜单
     */
    void packageLayoutMenu(List<MenuPo> layoutMenuPos);

    /**
     * 获取id为menuId页面内从菜单，也就是该菜单的子菜单，例如：页面内的按钮
     * menuId该菜单类型只能是菜单，如果是其他类型会抛出异常
     *
     * @param menuId 菜单id
     * @return List MenuPo.class
     */
    List<MenuPo> getPageInnerInAuth(Integer menuId);

    /**
     * 获取id为menuId页面内从菜单，也就是该菜单的子菜单，例如：页面内的按钮
     * menuId该菜单类型只能是菜单，如果是其他类型会抛出异常
     *
     * @param menuIds 菜单id
     * @return List MenuPo.class
     */
    List<MenuPo> getPageInnerInAuth(List<Integer> menuIds);

    /**
     * 获取菜单能力，如果当前租户具备该菜单能力MenuConstant.Ability的flag属性会设置为true
     *
     * @return Map<Integer, MenuConstant.Ability>
     */
    Map<Integer, Map<String, MenuAbilityVo>> listMenuAbilityMapInAuth();

    /**
     * 获取导出菜单数据，如果菜单为空会导出所有菜单
     *
     * @param menuIds 菜单id
     * @return List MenuPo.class
     */
    MenuItemsXml getExportDatum(List<Integer> menuIds);

    /**
     * 反序列化xml文件封装至MenuItemsXml对象中
     *
     * @param multipartFile xml文件数据
     * @return MenuItemsXml.class
     */
    MenuItemsXml getImportDatum(MultipartFile multipartFile);

    /**
     * 通过id获取父菜单
     *
     * @param childMenuIds 子菜单id
     * @return 父菜单 List MenuPo.class
     */
    List<MenuPo> getParentByIds(List<Integer> childMenuIds);

    /**
     * 通过id获取父菜单
     *
     * @param childMenuId 子菜单id
     * @return 父菜单 MenuPo.class
     */
    MenuPo getParentById(Integer childMenuId);

    /**
     * 通过父菜单id查询菜单
     *
     * @param parentMenuIds 父菜单id
     * @return List MenuPo.class
     */
    List<MenuPo> getByParentIds(List<Integer> parentMenuIds);

    /**
     * 通过父菜单id查询菜单
     *
     * @param parentMenuId 父菜单id
     * @return MenuPo.class
     */
    MenuPo getByParentId(Integer parentMenuId);

    /**
     * 通过类型编码获取菜单渲染规则
     *
     * @param typeCode 菜单类型编码
     * @return MenuRenderRuleVo.class
     */
    MenuRenderRuleVo getRenderRule(Integer typeCode);

    /**
     * 通过类型获取当前菜单支持的父菜单
     *
     * @param typeCode 菜单类型
     * @return List MenuPo.class
     */
    List<MenuPo> getSupportParent(Integer typeCode);

    /**
     * 通过clientUri查询菜单
     *
     * @param clientUri 客户端uri(路由uri)
     * @return MenuPo.class
     */
    MenuPo getByClientUri(String clientUri);

    /**
     * 获取指定类型菜单
     *
     * @param menuType 菜单类型
     * @return List MenuPo.class
     */
    List<MenuPo> getByTypeInAuth(MenuConstant.Type menuType);


}
